﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using WGM_Client_Net35V2.Properties;

namespace WGM_Client_Net35V2
{
    /// <summary>
    /// Used to manage all connection requests to the server as well as maintain the contact list.
    /// </summary>
    public class WGMLocalEngine: INotifyPropertyChanged
    {
        #region Class Data
        public bool IsBusy { get; private set; }
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>
        /// The current user's username.
        /// </summary>
        public string MyUsername { get; private set; }
        /// <summary>
        /// The current user's password
        /// </summary>
        public string MyPassword { get; private set; }
        /// <summary>
        /// An intial list of all online users.
        /// </summary>
        private string[] _onlineUsers;
        /// <summary>
        /// The date and time of the original connection to the server.
        /// Used to determine message validity.
        /// </summary>
        public DateTime LogonTime { get; private set; }
        /// <summary>
        /// Difference between local time and server time.
        /// </summary>
        public TimeSpan ServerTimeOffest { get; private set; }
        /// <summary>
        /// Set when a shutdown has been requested and cleared on completion.
        /// </summary>
        public bool ShutDownInProgress { get; private set; }
        /// <summary>
        /// Set when startup is in progress and cleared on completion.
        /// </summary>
        public bool StartupInProgress { get; private set; }
        /// <summary>
        /// The current user's online options.
        /// </summary>
        public WGM_Svc.UserOptions MyUserOptions { get; set; }
        /// <summary>
        /// The service object to connect to the server.
        /// </summary>
        private WGM_Svc.ServiceSoapClient _wgmSvc = new WGM_Client_Net35V2.WGM_Svc.ServiceSoapClient();
        /// <summary>
        /// Set when a retrieval is in progress and cleared on completion.
        /// </summary>
        public bool RetrievalInProgress { get; private set; }
        /// <summary>
        /// The local machines mac address. To be used in the future by the server.
        /// </summary>
        public string MyMacAddress { get; private set; }
        /// <summary>
        /// Current user's contact object to hold information like user image, etc.
        /// </summary>
        public ContactObject MyContactObject { get; private set; }
        /// <summary>
        /// List of all contacts.
        /// </summary>
        private ObservableCollection<ContactObject> _contacts = new ObservableCollection<ContactObject>();
        /// <summary>
        /// Manages access to the contact list for thread safety. Only one list manager should be used on a list.
        /// </summary>
        public ContactListManager ListManager { get; set; }
        /// <summary>
        /// Reference to the main display window.
        /// </summary>
        IMainWindow _mainWindow;
        /// <summary>
        /// Used to set the out going text box font settings.
        /// </summary>
        private LocalTextOptions _myLocalTextOptions = new LocalTextOptions();
        /// <summary>
        /// Event handler to update any UI objects bound to a code object.
        /// </summary>
        /// <param name="propertyName"></param>
        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        /// <summary>
        /// Local tect options accessor.
        /// </summary>
        public LocalTextOptions MyLocalTextOptions
        {
            get { return _myLocalTextOptions; }
            set
            {
                _myLocalTextOptions = value;
                OnPropertyChanged("MyLocalTextOptions");
            }
        }
        /// <summary>
        /// Used to update an image from a background thread. Needed to modify GUI objects by a background thread.
        /// </summary>
        /// <param name="co">Contact object to update.</param>
        /// <param name="imageArr">Image data</param>
        private delegate void ParameterizedImageDelegate(ContactObject co, byte[] imageArr);
        /// <summary>
        /// Accessor for the contact list.
        /// </summary>
        public ObservableCollection<ContactObject> AllContacts
        {
            get { return ListManager.ContactList; }
        }

        private ObservableCollection<NetworkObject> _allNetworks = new ObservableCollection<NetworkObject>();
        public ObservableCollection<NetworkObject> AllNetworks
        {
            get { return _allNetworks; }
            set
            {
                _allNetworks = value;
                OnPropertyChanged("AllNetworks");
            }
        }
         
        #endregion
        /// <summary>
        /// Constructor for the wgm engine.
        /// </summary>
        /// <param name="mainWindow">Interface to the main display window.</param>
        public WGMLocalEngine(IMainWindow mainWindow)
        {           
            _mainWindow = mainWindow;
            ListManager = new ContactListManager(_contacts);
            _wgmSvc.JoinNetworkCompleted += new EventHandler<WGM_Client_Net35V2.WGM_Svc.JoinNetworkCompletedEventArgs>(_wgmSvc_JoinNetworkCompleted);
            _wgmSvc.GetNetworksCompleted += new EventHandler<WGM_Client_Net35V2.WGM_Svc.GetNetworksCompletedEventArgs>(_wgmSvc_GetNetworksCompleted);
            _wgmSvc.LogInCompleted += new EventHandler<WGM_Client_Net35V2.WGM_Svc.LogInCompletedEventArgs>(_wgmSvc_LogInCompleted);
            _wgmSvc.GetAllUserOptionsCompleted += new EventHandler<WGM_Client_Net35V2.WGM_Svc.GetAllUserOptionsCompletedEventArgs>(_wgmSvc_GetAllUserOptionsCompleted);
            _wgmSvc.GetOnlineUserNamesCompleted += new EventHandler<WGM_Client_Net35V2.WGM_Svc.GetOnlineUserNamesCompletedEventArgs>(_wgmSvc_GetOnlineUserNamesCompleted);
            _wgmSvc.GetUserImageCompleted += new EventHandler<WGM_Client_Net35V2.WGM_Svc.GetUserImageCompletedEventArgs>(_wgmSvc_GetUserImageCompleted);
            _wgmSvc.GetUserOptionsCompleted += new EventHandler<WGM_Client_Net35V2.WGM_Svc.GetUserOptionsCompletedEventArgs>(_wgmSvc_GetUserOptionsCompleted);
            _wgmSvc.GetServerTimeCompleted += new EventHandler<WGM_Client_Net35V2.WGM_Svc.GetServerTimeCompletedEventArgs>(_wgmSvc_GetServerTimeCompleted);
            _wgmSvc.LogOffCompleted += new EventHandler<WGM_Client_Net35V2.WGM_Svc.LogOffCompletedEventArgs>(_wgmSvc_LogOffCompleted);
            _wgmSvc.RetrieveMessagesCompleted += new EventHandler<WGM_Client_Net35V2.WGM_Svc.RetrieveMessagesCompletedEventArgs>(_wgmSvc_RetrieveMessagesCompleted);
            _wgmSvc.SendMessageCompleted += new EventHandler<WGM_Client_Net35V2.WGM_Svc.SendMessageCompletedEventArgs>(_wgmSvc_SendMessageCompleted);
            _wgmSvc.SetUserImageCompleted += new EventHandler<WGM_Client_Net35V2.WGM_Svc.SetUserImageCompletedEventArgs>(_wgmSvc_SetUserImageCompleted);
            _wgmSvc.SetUserOptionsCompleted += new EventHandler<WGM_Client_Net35V2.WGM_Svc.SetUserOptionsCompletedEventArgs>(_wgmSvc_SetUserOptionsCompleted);
            MyMacAddress = GetMacAddress();
        }






        #region Async Callbacks
        void _wgmSvc_JoinNetworkCompleted(object sender, WGM_Client_Net35V2.WGM_Svc.JoinNetworkCompletedEventArgs e)
        {            
            NetworkObject no = e.UserState as NetworkObject;
            if (e.Error == null && e.Result != null)
            {

                _mainWindow.NetWorkJoinCompleted(true, e.message);
            }
        }
        void _wgmSvc_GetNetworksCompleted(object sender, WGM_Client_Net35V2.WGM_Svc.GetNetworksCompletedEventArgs e)
        {
            object[] loginParms = e.UserState as object[];
            if (e.Error == null && e.Result != null)
            {
                MyUsername = loginParms[0] as string;
                MyPassword = loginParms[1] as string;
                _wgmSvc.GetServerTimeAsync();
                _allNetworks.Clear();
                foreach (WGM_Svc.WGMNetwork network in e.Result)
                {
                    NetworkObject localNetwork = new NetworkObject(network.NetworkName, network.NetwordDescription, network.NetworkCreatedBy, network.NetworkCreationDate,network.IsPasswordProtected,network.IsRequesterAMember, network.NetworkMembersCount);
                    _allNetworks.Add(localNetwork);
                }
                _mainWindow.NetworksRetrievalCompleted(true, e.message);
            }
        }
        /// <summary>
        /// Callback for when Getting the server time is completed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _wgmSvc_GetServerTimeCompleted(object sender, WGM_Client_Net35V2.WGM_Svc.GetServerTimeCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                LogonTime = e.Result;
                ServerTimeOffest = DateTime.Now - LogonTime;
            }
            else
            {
                LogonTime = DateTime.Now;
                ServerTimeOffest = TimeSpan.FromSeconds(0);
            }
        }
        /// <summary>
        /// Callback for when setting the current user's online options is completed.
        /// If successful, then the local options will take effect.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e">Contains the options object passed in the user state.</param>
        void _wgmSvc_SetUserOptionsCompleted(object sender, WGM_Client_Net35V2.WGM_Svc.SetUserOptionsCompletedEventArgs e)
        {
            if (e.Error == null && e.UserState != null)
            {
                WGM_Svc.UserOptions myNewOptions = e.UserState as WGM_Svc.UserOptions;
                MyContactObject.ContactUserOptions = myNewOptions;
                MyLocalTextOptions = new LocalTextOptions(myNewOptions);
                OnPropertyChanged("MyFontColour");
                ListManager.SetLocalFontSettings();
            }
        }
        /// <summary>
        /// Callback for when the local user's image setting is completed.
        /// If successful, the local image will take effect.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e">Contains the image data in the user state.</param>
        void _wgmSvc_SetUserImageCompleted(object sender, WGM_Client_Net35V2.WGM_Svc.SetUserImageCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                if (e.UserState == null)
                {
                    MyContactObject.RemoveContactImage();
                }
                else
                {
                    byte[] imageArr = e.UserState as byte[];
                    MyContactObject.SetContactImage(HelperFunctions.GetBitmapFromImage(HelperFunctions.ByteArrayToImage(imageArr)));
                }
            }
        }
        /// <summary>
        /// Callback for when sending a message is completed.
        /// If unsuccessful, an failure message is sent to the local user to take action.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e">Contains the message object in the user state.</param>
        void _wgmSvc_SendMessageCompleted(object sender, WGM_Client_Net35V2.WGM_Svc.SendMessageCompletedEventArgs e)
        {
            if (e.Error != null || !( (e.Result.ToLower().Contains("success"))||(e.Result.ToLower().Contains("done")) ))
            {
                WGM_Svc.WGMMessage message = e.UserState as WGM_Svc.WGMMessage;
                if (message.MessageType == WGM_Client_Net35V2.WGM_Svc.MessageTypes.GLOBAL_MESSAGE)
                {
                    ContactObject co = ListManager.GetMainConversation();
                    ReceiveFailedTextMessage(co, message);
                }
                else
                {
                    ContactObject co = ListManager.GetContactObject(message.ReceiverLogon, false);
                    ReceiveFailedTextMessage(co, message);
                }
            }
        }
        /// <summary>
        /// Callback for when the message retrieval is completed.
        /// Action is taken depending on the message types.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _wgmSvc_RetrieveMessagesCompleted(object sender, WGM_Client_Net35V2.WGM_Svc.RetrieveMessagesCompletedEventArgs e)
        {
            
            RetrievalInProgress = false;

            
            if (e.Error == null && e.Result == true)
            {
                _mainWindow.MessageRetrievalCompleted(e.messages, e.Result, e.statusMessage);
                foreach (WGM_Svc.WGMMessage message in e.messages)
                {
                    switch (message.MessageType)
                    {
                        case WGM_Client_Net35V2.WGM_Svc.MessageTypes.GLOBAL_MESSAGE:
                        case WGM_Client_Net35V2.WGM_Svc.MessageTypes.PRIVATE_MESSAGE:
                            ReceiveTextMessage(message);
                            break;
                        case WGM_Client_Net35V2.WGM_Svc.MessageTypes.USER_LOGON:
                            if (ValidateMessage(message))
                            {
                                ReceiveUserLogonMessage(message);
                            }
                            break;
                        case WGM_Client_Net35V2.WGM_Svc.MessageTypes.USER_LEAVING:
                            if (ValidateMessage(message))
                            {
                                ReceiveUserLogoffMessage(message);
                            }
                            break;
                        case WGM_Client_Net35V2.WGM_Svc.MessageTypes.USER_CHANGED_INFO:
                            if (ValidateMessage(message))
                            {
                                if (!HelperFunctions.AreStringsEqual(message.SenderLogon, MyUsername, true))
                                    _wgmSvc.GetUserOptionsAsync(message.SenderLogon, MyUsername, MyPassword);
                            }
                            break;
                        case WGM_Client_Net35V2.WGM_Svc.MessageTypes.USER_CHANGED_IMAGE:
                            if (ValidateMessage(message))
                            {
                                if (!HelperFunctions.AreStringsEqual(message.SenderLogon, MyUsername, true))
                                    GetUserImage(message.SenderLogon);
                            }
                            break;
                        //case WGM_Client_Net35V2.WGM_Svc.MessageTypes.NEW_USER_CREATED:
                        //    if (ValidateMessage(message))
                        //    {
                        //        _wgmSvc.GetUserOptionsAsync(message.SenderLogon, MyUsername, MyPassword, true);
                        //    }
                        //    break;
                        default:
                            return;
                    }
                }
            }
            else
            {
                _mainWindow.MessageRetrievalCompleted(null, false, null);
            }
        }
        /// <summary>
        /// Callback for when logging off is completed.
        /// Calls the main window to inform it of the completion.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _wgmSvc_LogOffCompleted(object sender, WGM_Client_Net35V2.WGM_Svc.LogOffCompletedEventArgs e)
        {
            ShutDownInProgress = false;
            _mainWindow.LogoffCompleted();
            ListManager.ClearList();

        }
        /// <summary>
        /// Callback for when retrieving a contact's online options is completed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _wgmSvc_GetUserOptionsCompleted(object sender, WGM_Client_Net35V2.WGM_Svc.GetUserOptionsCompletedEventArgs e)
        {
            Console.WriteLine(DateTime.Now.ToString() + " _wgmSvc_GetUserOptionsCompleted");
            if (e.Error==null && e.Result != null)
            {                
                ContactObject co = ListManager.GetContactObject(e.Result.UserName, false);                
                if (co != null)
                {
                    Console.WriteLine(DateTime.Now.ToString() + " _wgmSvc_GetUserOptionsCompleted " +co.ContactUserName);
                    ListManager.SetContactPosition(co);
                    co.ContactUserOptions = e.Result;
                }
                else if(e.UserState != null && ((bool)e.UserState == true))
                {
                    co = new ContactObject(e.Result.DisplayName, e.Result.UserStatus, e.Result.UserName, e.Result, false, false, this);
                    co.OutgoingTextBox.Tag = co;
                    co.OutgoingTextBox.KeyUp += new System.Windows.Input.KeyEventHandler(OutgoingTextBox_KeyUp);
                    co.ContactIsOnline = true;
                    co.SetLocalFontSettings();
                    ListManager.SetContactPosition(co);
                }               

            }
        }
        /// <summary>
        /// Callback for when retrieving a user's image is completed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _wgmSvc_GetUserImageCompleted(object sender, WGM_Client_Net35V2.WGM_Svc.GetUserImageCompletedEventArgs e)
        {
            Console.WriteLine(DateTime.Now.ToString() + " _wgmSvc_GetUserImageCompleted");
            if (e.Error == null && e.Result != null)
            {
                ContactObject co = e.UserState as ContactObject;
                Console.WriteLine(DateTime.Now.ToString() + " _wgmSvc_GetUserImageCompleted "+co.ContactUserName);
                co.SetContactImage(HelperFunctions.GetBitmapFromImage(HelperFunctions.ByteArrayToImage(e.Result)));
            }
        }
        /// <summary>
        /// Callback for when retrieving all the the users is completed. This builds the contact list.
        /// Informs the main window so it can take actions.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _wgmSvc_GetAllUserOptionsCompleted(object sender, WGM_Client_Net35V2.WGM_Svc.GetAllUserOptionsCompletedEventArgs e)
        {
            Console.WriteLine(DateTime.Now.ToString() + " _wgmSvc_GetAllUserOptionsCompleted");
            if (e.Error != null)
            {
                _mainWindow.StartupCompleted(false);
                return;
            }
            ListManager.ClearList();
            ContactObject newCo = new ContactObject("Main Conversation", "group messages", "main_conversation", null, true,false, this);
            newCo.OutgoingTextBox.Tag = newCo;
            newCo.OutgoingTextBox.KeyUp += new System.Windows.Input.KeyEventHandler(OutgoingTextBox_KeyUp);

            ListManager.AddContactObject(newCo);
            
            foreach (WGM_Svc.UserOptions uo in e.Result)
            {
                if (HelperFunctions.AreStringsEqual(uo.UserName, MyUsername, true))
                {
                    MyUserOptions = uo;
                    newCo = new ContactObject(uo.DisplayName, uo.UserStatus, uo.UserName, uo, false,true, this);
                    MyContactObject = newCo;
                    _mainWindow.MyContactObjectChanged();
                    MyLocalTextOptions = new LocalTextOptions(uo);
                    OnPropertyChanged("MyFontColour");
                    continue;
                }
                newCo = new ContactObject(uo.DisplayName, uo.UserStatus, uo.UserName, uo, false,false, this);
                newCo.OutgoingTextBox.Tag = newCo;
                newCo.OutgoingTextBox.KeyUp += new System.Windows.Input.KeyEventHandler(OutgoingTextBox_KeyUp);

                ListManager.AddContactObject(newCo);
                
            }
            ListManager.SetSelectedContact(0);

            _mainWindow.StartupCompleted(true);
            ListManager.SetLocalFontSettings();
            //_wgmSvc.GetOnlineUserNamesAsync();

        }
        /// <summary>
        /// Callback for when logging on is completed. Informs the mainwindow so it can take actions.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _wgmSvc_LogInCompleted(object sender, WGM_Client_Net35V2.WGM_Svc.LogInCompletedEventArgs e)
        {
            Console.WriteLine(DateTime.Now.ToString() + " _wgmSvc_UserLoginCompleted");
            if (e.Error==null && e.UserState != null)
            {
                object[] loginParms = e.UserState as object[];
                if (e.Result == true)
                {
                    MyUsername = loginParms[0] as string;
                    MyPassword = loginParms[1] as string;
                    _wgmSvc.GetServerTimeAsync();
                }
                _mainWindow.LoginCompleted(e.Result, e.message, (bool)(loginParms[3]));
            }
            else
            {
                _mainWindow.LoginCompleted(false, "Something went horribly wrong",false);
            }
            StartupInProgress = false;

        }
        /// <summary>
        /// Callback for when retrieving all online user names is completed.
        /// Then sets all contacts in the contact list to either online or offline based on this list.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _wgmSvc_GetOnlineUserNamesCompleted(object sender, WGM_Client_Net35V2.WGM_Svc.GetOnlineUserNamesCompletedEventArgs e)
        {
            Console.WriteLine(DateTime.Now.ToString() + " _wgmSvc_GetOnlineUserNamesCompleted");
            if (e.Error == null && e.Result != null)
            {
                _onlineUsers = e.Result.ToArray();
                ListManager.SetOnlineUsers(_onlineUsers);
            }
        }


        #endregion          
        /// <summary>
        /// Get's the display image for a single user.
        /// </summary>
        /// <param name="username">The username of the contact image to retrieve.</param>
        public void GetUserImage(string username)
        {
            BackgroundWorker singleImageWorker = new BackgroundWorker();
            singleImageWorker.DoWork += new DoWorkEventHandler(singleImageWorker_DoWork);
            object[] parms = { username, Application.Current.Dispatcher };
            singleImageWorker.RunWorkerAsync(parms);
        }      
        /// <summary>
        /// The background worker's procedure to retrieve a single user's image.
        /// Uses a delegate to update the image since it modifies a GUI object which will throw an exception.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void singleImageWorker_DoWork(object sender, DoWorkEventArgs e)
        {

            try
            {
                object[] parms = e.Argument as object[];
                string username = parms[0] as string;
                Dispatcher dispatcher = parms[1] as Dispatcher;
                string message = "";
                Console.WriteLine(DateTime.Now.ToString() + " singleImageWorker_DoWork " + username);
                byte[] result = _wgmSvc.GetUserImage(username, MyUsername, MyPassword, out message);
                ParameterizedImageDelegate updateImage = new ParameterizedImageDelegate(UpdateUserImage);
                dispatcher.BeginInvoke(updateImage, ListManager.GetContactObject(username, false), result);
            }
            catch { }
        }
        /// <summary>
        /// Get's the entire contact list's display images.
        /// </summary>
        public void GetAllUserImages()
        {
            Console.WriteLine(DateTime.Now.ToString() + " GetAllUserImages");
            BackgroundWorker imageWorker = new BackgroundWorker();
            imageWorker.DoWork += new DoWorkEventHandler(imageWorker_DoWork);
            imageWorker.RunWorkerAsync(Application.Current.Dispatcher);
        }
        /// <summary>
        /// Background worker's procedure. This one used to update the images of all contacts.
        /// Requires a delegate because a background thread is modifying a GUI object which will throw an exception.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void imageWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                Dispatcher dispatcher = e.Argument as Dispatcher;
                string message = "";
                byte[] result = _wgmSvc.GetUserImage(MyUsername, MyUsername, MyPassword, out message);
                ParameterizedImageDelegate updateImage = new ParameterizedImageDelegate(UpdateUserImage);
                dispatcher.BeginInvoke(updateImage, MyContactObject, result);


                for (int count = 0; count < ListManager.ContactList.Count; count++)
                {
                    if (ListManager.ContactList[count].IsMainWindow)
                        continue;
                    result = _wgmSvc.GetUserImage(ListManager.ContactList[count].ContactUserName, MyUsername, MyPassword, out message);
                    dispatcher.BeginInvoke(updateImage, ListManager.ContactList[count], result);
                }
            }
            catch { }
            
            
        }
        /// <summary>
        /// Used to set a cotnact object's image.
        /// </summary>
        /// <param name="co">The contact object to update.</param>
        /// <param name="imageArr">The new image data. Null to clear the contact image.</param>
        private void UpdateUserImage(ContactObject co, byte[] imageArr)
        {
            if (imageArr == null)
                co.RemoveContactImage();
            else
                co.SetContactImage(HelperFunctions.GetBitmapFromImage(HelperFunctions.ByteArrayToImage(imageArr)));

            if (HelperFunctions.AreStringsEqual(co.ContactUserName, MyUsername, true))
            {
                _mainWindow.MyDisplayImageChanged();
            }
        }        
        /// <summary>
        /// When a user logs off of the system.
        /// </summary>
        /// <param name="message">The actual server message.</param>
        private void ReceiveUserLogoffMessage(WGM_Client_Net35V2.WGM_Svc.WGMMessage message)
        {
            ContactObject co = ListManager.GetContactObject(message.SenderLogon, false);
            if (co != null && co.ContactIsMe == false)
            {
                co.ContactIsOnline = false;
                _mainWindow.ContactChangedOnlineStatus(co);
            }
        }
        /// <summary>
        /// When a user logs on to the system.
        /// </summary>
        /// <param name="message">The actual server message</param>
        private void ReceiveUserLogonMessage(WGM_Client_Net35V2.WGM_Svc.WGMMessage message)
        {
            ContactObject co = ListManager.GetContactObject(message.SenderLogon, false);
            if (co != null && co.ContactIsMe == false)
            {
                co.ContactIsOnline = true;
                _mainWindow.ContactChangedOnlineStatus(co);
            }
        }
        /// <summary>
        /// Determine is a message is relevant.
        /// Ex. If a user online message was sent before the current user logged on, then this message is not valid
        /// as it is outdated.
        /// </summary>
        /// <param name="message">The actual server message</param>
        /// <returns>Value indicating whether the message is relevant.</returns>
        private bool ValidateMessage(WGM_Client_Net35V2.WGM_Svc.WGMMessage message)
        {
            if (message.TimeStamp < LogonTime)
                return false;


            return true;
        }
        /// <summary>
        /// Called when a send message fails. Allows the user to resend the message.
        /// </summary>
        /// <param name="destination">Contact object in which to place the message.</param>
        /// <param name="message">The actual server message.</param>
        private void ReceiveFailedTextMessage(ContactObject destination, WGM_Svc.WGMMessage message)
        {
            if (message == null)
                return;
            Paragraph para = new Paragraph();

            SolidColorBrush introBrush = new SolidColorBrush(Colors.Red);
            SolidColorBrush messageBrush = new SolidColorBrush(HelperFunctions.GetNewColour(MyUserOptions));
            string runIntro = "";
            if (Properties.Settings.Default.DisplayDateTimeStamp == true)
                runIntro += "<" + message.TimeStamp.ToString() + "> ";

            Run introRun = new Run(runIntro + "Connection Error - Unable to send message:\r\n");
            introRun.FontFamily = Properties.Settings.Default.SystemFontFamily;
            introRun.FontSize = Properties.Settings.Default.SystemFontSize;
            if (Settings.Default.IsSystemFontBold)
                introRun.FontWeight = FontWeights.Bold;
            if (Settings.Default.IsSystemFontItalics)
                introRun.FontStyle = FontStyles.Italic;
            if (Settings.Default.IsSystemFontUnderlined)
                introRun.TextDecorations = TextDecorations.Underline;

            introRun.Foreground = introBrush;
            para.Inlines.Add(introRun);
            string messageContent = message.Message as string;
            string[] delim = { "~!~@delim@~!~" };
            Regex linkMatch = new Regex("(?:^|[\\s\\[\\]\\}\\{\\(\\)\\\'\\\"<>])((?:(?:https?|gopher|ftp|file|irc):\\/\\/|www\\.)[a-zA-Z0-9\\.\\-=;&%\\?]+(?:\\/?[a-zA-Z0-9\\.\\-=;:'{}><,~`*_#$+&%\\?]*)*)");
            MatchCollection matches = linkMatch.Matches(messageContent);

            foreach (Match link in matches)
            {
                messageContent = messageContent.Replace(link.Value, delim[0]);
            }
            string[] messageRuns = messageContent.Split(delim, StringSplitOptions.None);
            int insCount = 0;
            foreach (string messageText in messageRuns)
            {
                Run messageRun = new Run(messageText);
                messageRun.FontFamily = new FontFamily(MyUserOptions.UserFontFace);
                messageRun.FontSize = MyUserOptions.FontSize;
                if (MyUserOptions.IsFontBold)
                    messageRun.FontWeight = FontWeights.Bold;
                if (MyUserOptions.IsFontItalics)
                    messageRun.FontStyle = FontStyles.Italic;
                if (MyUserOptions.IsFontUnderlined)
                    messageRun.TextDecorations = TextDecorations.Underline;

                messageRun.Foreground = messageBrush;
                para.Inlines.Add(messageRun);

                if (insCount < matches.Count)
                {
                    Hyperlink hp = new Hyperlink(new Run(matches[insCount].Value));
                    hp.FontFamily = messageRun.FontFamily;
                    hp.FontSize = messageRun.FontSize + 1;
                    hp.FontStyle = messageRun.FontStyle;
                    hp.TextDecorations = messageRun.TextDecorations;
                    hp.Foreground = new SolidColorBrush(Settings.Default.HyperlinkColour);
                    hp.Click += new RoutedEventHandler(hp_Click);
                    hp.Tag = matches[insCount].Value.Trim();
                    para.Inlines.Add(hp);
                }
                insCount++;
            }
            Hyperlink resendLink = new Hyperlink(new Run("\r\n\r\nClick here to resend"));
            resendLink.FontFamily = introRun.FontFamily;
            resendLink.FontStyle = introRun.FontStyle;
            resendLink.FontSize = introRun.FontSize;
            resendLink.TextDecorations = introRun.TextDecorations;
            resendLink.Foreground = new SolidColorBrush(Settings.Default.HyperlinkColour);
            resendLink.Click += new RoutedEventHandler(resendLink_Click);
            resendLink.Tag = message;
            para.Inlines.Add(resendLink);
            destination.IncomingTextBox.Document.Blocks.Add(para);

            destination.HasMessagesVisibility = Visibility.Visible;
            _mainWindow.ReceiveTextMessage(destination,MyContactObject, message);
        }
        void resendLink_Click(object sender, RoutedEventArgs e)
        {
            Hyperlink link = sender as Hyperlink;
            //_wgmSvc.SendMessageAsync(link.Tag as WGM_Svc.WGMMessage, MyUsername, MyPassword, link.Tag as WGM_Svc.WGMMessage);

        }
        /// <summary>
        /// Receives a text message and routes it to the correct contact object or main window as the case might be.
        /// </summary>
        /// <param name="message">The actual message object from the server.</param>
        private void ReceiveTextMessage(WGM_Client_Net35V2.WGM_Svc.WGMMessage message)
        {
            ContactObject destination = null;
            ContactObject sender = null;

            if (message.MessageType == WGM_Client_Net35V2.WGM_Svc.MessageTypes.GLOBAL_MESSAGE)
            {
                destination = ListManager.GetMainConversation();
                if (HelperFunctions.AreStringsEqual(MyUsername, message.SenderLogon, true))
                    sender = MyContactObject;
                else
                    sender = ListManager.GetContactObject(message.SenderLogon, false);
            }
            else
            {
                if (HelperFunctions.AreStringsEqual(MyUsername, message.SenderLogon, true))
                {
                    sender = MyContactObject;
                    destination = ListManager.GetContactObject(message.ReceiverLogon, false);
                }
                else
                {
                    sender = destination = ListManager.GetContactObject(message.SenderLogon, false);
                }

            }
            if (destination != null && sender != null)
            {
                Paragraph para = new Paragraph();

                SolidColorBrush introBrush = new SolidColorBrush(Color.FromRgb((byte)Properties.Settings.Default.SystemFontColorR,
                                                             (byte)Properties.Settings.Default.SystemFontColorG,
                                                             (byte)Properties.Settings.Default.SystemFontColorB));
                SolidColorBrush overrideBrush = new SolidColorBrush(Color.FromRgb((byte)Settings.Default.OverrideIncomingFontColourR,
                                                     (byte)Settings.Default.OverrideIncomingFontColourG,
                                                     (byte)Settings.Default.OverrideIncomingFontColourB));

                SolidColorBrush messageBrush = new SolidColorBrush(HelperFunctions.GetNewColour(sender.ContactUserOptions));

                bool overrideIncoming = Settings.Default.OverrideIncomingFonts && !HelperFunctions.AreStringsEqual(MyUsername, message.SenderLogon, true);

                if (overrideIncoming)
                    messageBrush = overrideBrush;

                string runIntro = "";
                if (Properties.Settings.Default.DisplayDateTimeStamp == true)
                    runIntro += "<" + message.TimeStamp.ToString() + "> ";

                Run introRun = new Run(runIntro + HelperFunctions.GetIntroString(sender.ContactUserOptions) + " says:\r\n");
                introRun.FontFamily = Properties.Settings.Default.SystemFontFamily;
                introRun.FontSize = Properties.Settings.Default.SystemFontSize;
                if (Settings.Default.IsSystemFontBold)
                    introRun.FontWeight = FontWeights.Bold;
                if (Settings.Default.IsSystemFontItalics)
                    introRun.FontStyle = FontStyles.Italic;
                if (Settings.Default.IsSystemFontUnderlined)
                    introRun.TextDecorations = TextDecorations.Underline;

                introRun.Foreground = introBrush;

                para.Inlines.Add(introRun);



                string messageContent = message.Message as string;

                string[] delim = { "~!~@delim@~!~" };
                Regex linkMatch = new Regex("(?:^|[\\s\\[\\]\\}\\{\\(\\)\\\'\\\"<>])((?:(?:https?|gopher|ftp|file|irc):\\/\\/|www\\.)[a-zA-Z0-9\\.\\-=;&%\\?]+(?:\\/?[a-zA-Z0-9\\.\\-=;:'{}><,~`*_#$+&%\\?]*)*)");
                MatchCollection matches = linkMatch.Matches(messageContent);

                foreach (Match link in matches)
                {
                    messageContent = messageContent.Replace(link.Value, delim[0]);
                }
                string[] messageRuns = messageContent.Split(delim, StringSplitOptions.None);
                int insCount = 0;
                foreach (string messageText in messageRuns)
                {
                    Run messageRun = new Run(messageText);
                    messageRun.FontFamily = new FontFamily(sender.ContactUserOptions.UserFontFace);
                    messageRun.FontSize = sender.ContactUserOptions.FontSize;
                    if (sender.ContactUserOptions.IsFontBold)
                        messageRun.FontWeight = FontWeights.Bold;
                    if (sender.ContactUserOptions.IsFontItalics)
                        messageRun.FontStyle = FontStyles.Italic;
                    if (sender.ContactUserOptions.IsFontUnderlined)
                        messageRun.TextDecorations = TextDecorations.Underline;

                    if (overrideIncoming)
                    {
                        messageRun.FontFamily = Settings.Default.OverrideIncomingFontFamily;
                        messageRun.FontSize = Settings.Default.OverrideIncomingFontSize;
                        if (Settings.Default.IsOverrideFontBold)
                            messageRun.FontWeight = FontWeights.Bold;
                        if (Settings.Default.IsOverrideFontItalics)
                            messageRun.FontStyle = FontStyles.Italic;
                        if (Settings.Default.IsOverrideFontUnderlined)
                            messageRun.TextDecorations = TextDecorations.Underline;
                    }

                    messageRun.Foreground = messageBrush;
                    para.Inlines.Add(messageRun);

                    if (insCount < matches.Count)
                    {
                        Hyperlink hp = new Hyperlink(new Run(matches[insCount].Value));
                        hp.FontFamily = messageRun.FontFamily;
                        hp.FontSize = messageRun.FontSize + 1;
                        hp.FontStyle = messageRun.FontStyle;
                        hp.TextDecorations = messageRun.TextDecorations;
                        hp.Foreground = new SolidColorBrush(Settings.Default.HyperlinkColour);
                        hp.Click += new RoutedEventHandler(hp_Click);
                        hp.Tag = matches[insCount].Value.Trim();
                        para.Inlines.Add(hp);
                    }
                    insCount++;
                }

                destination.IncomingTextBox.Document.Blocks.Add(para);

                destination.HasMessagesVisibility = Visibility.Visible;
                _mainWindow.ReceiveTextMessage(destination, sender, message);
            }
            else
            {
                _mainWindow.MessageRetrievalCompleted(null, false, "System error");
            }
        }
        /// <summary>
        /// Event handler to determine when the user hits just the enter key to send a message.
        /// If a control key is used with the enter button, a new line is inserted.
        /// </summary>
        /// <param name="sender">Richtext box that generated the event. Tagged with the contact object to send this message to</param>
        /// <param name="e"></param>
        void OutgoingTextBox_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
        {
            ContactObject co = (sender as RichTextBox).Tag as ContactObject;
            if (e.Key == Key.Enter && e.KeyboardDevice.Modifiers == ModifierKeys.Control)
            {
                co.OutgoingTextBox.CaretPosition.InsertLineBreak();
                co.OutgoingTextBox.CaretPosition = co.OutgoingTextBox.CaretPosition.DocumentEnd;

            }
            if (e.Key == Key.Enter && e.KeyboardDevice.Modifiers == ModifierKeys.None)
            {
                TextPointer from = co.OutgoingTextBox.CaretPosition.DocumentStart;
                TextPointer to = co.OutgoingTextBox.CaretPosition;
                co.OutgoingTextBox.Selection.Select(from, to);
                co.OutgoingTextBox.Selection.Text = co.OutgoingTextBox.Selection.Text.Remove(co.OutgoingTextBox.Selection.Text.Length - 2, 2);
                co.OutgoingTextBox.SelectAll();
                string messageText = co.OutgoingTextBox.Selection.Text;

                WGM_Svc.WGMMessage newMessage = new WGM_Client_Net35V2.WGM_Svc.WGMMessage();
                if (co.IsMainWindow)
                {
                    newMessage.MessageType = WGM_Client_Net35V2.WGM_Svc.MessageTypes.GLOBAL_MESSAGE;
                    newMessage.SenderLogon = MyUsername;
                    newMessage.Message = messageText.Trim();
                }
                else
                {
                    newMessage.MessageType = WGM_Client_Net35V2.WGM_Svc.MessageTypes.PRIVATE_MESSAGE;
                    newMessage.SenderLogon = MyUsername;
                    newMessage.ReceiverLogon = co.ContactUserName;
                    newMessage.Message = messageText.Trim();
                }
                
                co.OutgoingTextBox.Selection.Text = "";
                if ((newMessage.Message as string).Trim() == "")
                    return;
                //_wgmSvc.SendMessageAsync(newMessage, MyUsername, MyPassword, newMessage);
            }
        }
        /// <summary>
        /// Event handler for a hyperlink clicked in an incoming message box.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void hp_Click(object sender, RoutedEventArgs e)
        {
            Hyperlink link = sender as Hyperlink;
            try
            {
                Process.Start(link.Tag.ToString());
            }
            catch { }
        }
        /// <summary>
        /// Login to the server asynchronously.
        /// </summary>
        /// <param name="userName">Username used to access the server</param>
        /// <param name="password">Password used to access the server</param>
        /// <param name="isReconnect">Is this a reconnection attempt after a connection loss?</param>
        public void DoLogin(string userName, string password, bool isReconnect)
        {
            if (!ShutDownInProgress)
            {
                StartupInProgress = true;
                object[] loginParms = { userName, password, MyMacAddress, isReconnect };
                _wgmSvc.GetNetworksAsync(userName, password, loginParms);
            }
        }
        /// <summary>
        /// Asynchronously logs off from the server.
        /// If this is not called and the user closes the application by some external means,
        /// the server will detect the user is offline after about 20 seconds.
        /// </summary>
        public void DoLogoff()
        {
            if (!ShutDownInProgress)
            {
                ShutDownInProgress = true;
                _wgmSvc.LogOffAsync(MyUsername, MyPassword, MyMacAddress, "Normal Logoff");
            }
        }
        /// <summary>
        /// Asynchronously retrieves all server messages for the current user.
        /// </summary>
        public void RetrieveMessages()
        {
            if (RetrievalInProgress || ShutDownInProgress || StartupInProgress)
                return;
            RetrievalInProgress = true;
            //_wgmSvc.RetrieveMessagesAsync(MyUsername, MyPassword, MyMacAddress);
        }
        /// <summary>
        /// Used to retrieve the current Mac address of the local computer.
        /// Not currently used, but future server updates will require this.
        /// </summary>
        /// <returns>Local computer's mac address.</returns>
        private string GetMacAddress()
        {
            foreach (NetworkInterface nif in NetworkInterface.GetAllNetworkInterfaces())
            {
                if (nif.OperationalStatus == OperationalStatus.Up)
                {
                    return nif.GetPhysicalAddress().ToString();
                }
            }
            return "";
        }
        /// <summary>
        /// This function must be called first to retrieve the contact list and online users.
        /// </summary>
        public void StartExecuting()
        {
            if (!ShutDownInProgress)
                _wgmSvc.GetAllUserOptionsAsync(MyUsername, MyPassword);
        }
        /// <summary>
        /// Disconnect and abort any current connections to the server and clear the contact list.
        /// </summary>
        public void Shutdown()
        {
            ShutDownInProgress = true;
            _wgmSvc.Abort();
            ListManager.ClearList();
            _onlineUsers = null;
            ShutDownInProgress = false;
        }
        /// <summary>
        /// Event generated when a contact's selection status changes.
        /// </summary>
        /// <param name="contactObject">Changing contact</param>
        internal void TabSelectionChanged(ContactObject contactObject)
        {
            _mainWindow.TabSelectionChanged(contactObject);
        }
        /// <summary>
        /// Event generated when a contact's unread messages changed.
        /// </summary>
        /// <param name="contactObject">Changing object</param>
        internal void TabHasUnreadChanged(ContactObject contactObject)
        {
            _mainWindow.TabHasUnreadChanged(contactObject);
        }
        /// <summary>
        /// Sets the current user's online options asynchronously.
        /// Only on successfull completion will the new options take effect locally.
        /// </summary>
        /// <param name="myNewOptions">New online options to set.</param>
        internal void SetMyUserOptions(WGM_Client_Net35V2.WGM_Svc.UserOptions myNewOptions)
        {
            _wgmSvc.SetUserOptionsAsync(MyUsername, MyPassword, myNewOptions,myNewOptions);
        }
        /// <summary>
        /// Sets the current user's display image asynchronously.
        /// If a null image is passed in, the user image is cleared.
        /// Only on successfull completion will the new image take effect locally.
        /// </summary>
        /// <param name="tempImage">Image to set, null to remove current image</param>
        internal void SetMyDisplayImage(System.Drawing.Image tempImage)
        {
            if (tempImage != null)
            {
                byte[] imageArr = HelperFunctions.ImageToByteArray(tempImage, System.Drawing.Imaging.ImageFormat.Bmp);
                _wgmSvc.SetUserImageAsync(MyUsername, MyPassword, imageArr,imageArr);
            }
            else
            {
                _wgmSvc.SetUserImageAsync(MyUsername, MyPassword, null,null);
            }
        }

        internal void JoinNetwork(string networkName)
        {
            IsBusy = true;
            NetworkObject no = GetNetworkObjectByName(networkName);
            if (no != null)
            {
                no.SetStatusMessage("Joining network...");
                _wgmSvc.JoinNetworkAsync(MyUsername, MyPassword, no.NetworkName, no.NetworkPassword, no);
            }
            else
            {
                _mainWindow.NetWorkJoinCompleted(false, "No such network");
            }
        }

        private NetworkObject GetNetworkObjectByName(string networkName)
        {
            foreach (NetworkObject no in AllNetworks)
            {
                if (HelperFunctions.AreStringsEqual(networkName, no.NetworkName, true))
                    return no;
            }
            return null;
        }
    }
    /// <summary>
    /// Manages a contact list to provide thread safety over the list.
    /// Do NOT use more than one list manager on a contact list!
    /// </summary>
    public class ContactListManager
    {
        #region Class Data.
        /// <summary>
        /// List to manage.
        /// </summary>
        public ObservableCollection<ContactObject> ContactList { get; private set; }
        /// <summary>
        /// Used for thread safety.
        /// </summary>
        private Mutex _listMutex = new Mutex();

        #endregion
        /// <summary>
        /// Constructer for the ListManager.
        /// </summary>
        /// <param name="contactListToManage">The contact list to manage.</param>
        public ContactListManager(ObservableCollection<ContactObject> contactListToManage)
        {
            ContactList = contactListToManage;
            if (ContactList == null)
                ContactList = new ObservableCollection<ContactObject>();
        }
        /// <summary>
        /// Clears the list of all contacts.
        /// </summary>
        public void ClearList()
        {
            _listMutex.WaitOne();
            Console.WriteLine(DateTime.Now.ToString() + " ClearList");
            ContactList.Clear();
            _listMutex.ReleaseMutex();
        }
        /// <summary>
        /// Adds a new contact to the list.
        /// Does NOT check to see if an existing contact with the same username exists.
        /// </summary>
        /// <param name="co">Contact to add.</param>
        public void AddContactObject(ContactObject co)
        {
            _listMutex.WaitOne();
            Console.WriteLine(DateTime.Now.ToString() + " AddContactObject " + co.ContactUserName);
            ContactList.Add(co);
            _listMutex.ReleaseMutex();
        }
        /// <summary>
        /// Removes a contact object from the list of contacts.
        /// Does nothing if contact does not exist.
        /// </summary>
        /// <param name="contact">Contact object to remove.</param>
        public void RemoveContactObject(ContactObject contact)
        {
            _listMutex.WaitOne();
            Console.WriteLine(DateTime.Now.ToString() + " RemoveContactObject " + contact.ContactUserName);
            for (int count = 0; count < ContactList.Count; count++)
            {
                if (HelperFunctions.AreStringsEqual(ContactList[count].ContactUserName, contact.ContactUserName, true))
                {
                    ContactList.RemoveAt(count);
                    break;
                }
            }            
            _listMutex.ReleaseMutex();
        }
        /// <summary>
        /// Gets a contact object based on the user name.
        /// Returns null if the contact does not exist.
        /// </summary>
        /// <param name="userName">Username to search for</param>
        /// <param name="remove">Removes the contact from the list if true</param>
        /// <returns>The contact object, null if not found</returns>
        public ContactObject GetContactObject(string userName, bool remove)
        {
            ContactObject retVal = null;
            _listMutex.WaitOne();
            Console.WriteLine(DateTime.Now.ToString() + " GetContactObject " + userName);
            for (int count = 0; count < ContactList.Count; count++)
            {
                if (HelperFunctions.AreStringsEqual(ContactList[count].ContactUserName, userName, true))
                {
                    retVal = ContactList[count];
                    if(remove)
                        ContactList.RemoveAt(count);                   
                }
            }
            _listMutex.ReleaseMutex();
            return retVal;
        }
        /// <summary>
        /// Get's the main conversation window
        /// </summary>
        /// <returns>The main conversation window</returns>
        public ContactObject GetMainConversation()
        {
            ContactObject retVal = null;
            _listMutex.WaitOne();
            Console.WriteLine(DateTime.Now.ToString() + " GetMainConversation ");
            for (int count = 0; count < ContactList.Count; count++)
            {
                if (ContactList[count].IsMainWindow)
                {
                    retVal = ContactList[count];
                    break;
                }
            }
            _listMutex.ReleaseMutex();
            return retVal;
        }
        /// <summary>
        /// Set a contact to be the active contact
        /// </summary>
        /// <param name="index">Contact index to set</param>
        public void SetSelectedContact(int index)
        {
            _listMutex.WaitOne();
            Console.WriteLine(DateTime.Now.ToString() + " SetSelectedContact " + index);
            if (index > -1 && index < ContactList.Count)
                ContactList[index].IsSelected = true;
            _listMutex.ReleaseMutex();
        }
        /// <summary>
        /// Set a contact's position in alphabetical order in the contact list.
        /// If the contact is not in the list it is added to the list.
        /// </summary>
        /// <param name="co">Contact to set</param>
        public void SetContactPosition(ContactObject co)
        {
            _listMutex.WaitOne();
            Console.WriteLine(DateTime.Now.ToString() + " SetContactPosition " + co.ContactUserName);
            ContactList.Remove(co);
            for (int count = 1; count < ContactList.Count; count++)
            {
                ContactObject curr = ContactList[count];
                if (string.Compare(co.ContactDisplayName.Trim(), curr.ContactDisplayName.Trim(), true) < 0)
                {
                    ContactList.Insert(count, co);
                    _listMutex.ReleaseMutex();
                    return;
                }
            }
            ContactList.Add(co);
            _listMutex.ReleaseMutex();
        }
        /// <summary>
        /// Set all users in the contact list to online or offline depending on a list of online users
        /// </summary>
        /// <param name="onlineUsers">List of online users</param>
        public void SetOnlineUsers(string[] onlineUsers)
        {
            _listMutex.WaitOne();
            Console.WriteLine(DateTime.Now.ToString() + " SetOnlineUsers");
            for (int count = 0; count < ContactList.Count;count++)
            {
                if (ContactList[count].IsMainWindow)
                    continue;
                bool isOnline = IsUserOnline(ContactList[count].ContactUserName, onlineUsers);
                if (ContactList[count].ContactIsOnline != isOnline)
                {
                    ContactList[count].ContactIsOnline = isOnline;
                }
            }
            _listMutex.ReleaseMutex();
        }
        /// <summary>
        /// Check to see if a user is online out of a specified list of online users
        /// </summary>
        /// <param name="userName">Username to check</param>
        /// <param name="onlineUsers">List of online users</param>
        /// <returns>Value indicating whether or not the user is in the online list</returns>
        private bool IsUserOnline(string userName, string[] onlineUsers)
        {
            if (onlineUsers == null)
                return false;
            foreach (string onlineUser in onlineUsers)
            {
                if (HelperFunctions.AreStringsEqual(onlineUser, userName, true))
                    return true;
            }
            return false;
        }
        /// <summary>
        /// Used to make sure all of the outgoing textboxes are using your fonts.
        /// </summary>
        public void SetLocalFontSettings()
        {
            _listMutex.WaitOne();
            Console.WriteLine(DateTime.Now.ToString() + " SetLocalFontSettings");
            for (int count = 0; count < ContactList.Count; count++ )
            {
                ContactList[count].SetLocalFontSettings();
            }
            _listMutex.ReleaseMutex();

        }
    }
}
